#include "memory.h"

/* Global var */
unsigned long mem_allocated;    /* Total memory used in Bytes */

void *xalloc(unsigned long size)
{
    void *mem;
    if ((mem = malloc(size)))
        mem_allocated += size;
    return mem;
}

void *zalloc(unsigned long size)
{
    void *mem;
    if ((mem = malloc(size))) {
        memset(mem, 0, size);
        mem_allocated += size;
    }
    return mem;
}

void xfree(void *p)
{
    mem_allocated -= sizeof (p);
    free(p);
    p = NULL;
}

/*
 * Memory management. in debug mode,
 * help finding eventual memory leak.
 * Allocation memory types manipulated are :
 *
 * +type+--------meaning--------+
 * ! 0  ! Free slot             !
 * ! 1  ! Overrun               !
 * ! 2  ! free null             !
 * ! 3  ! realloc null          !
 * ! 4  ! Not previus allocated !
 * ! 8  ! Last free list        !
 * ! 9  ! Allocated             !
 * +----+-----------------------+
 *
 * global variabel debug bit 9 ( 512 ) used to
 * flag some memory error.
 *
 */

#ifdef _DEBUG_

typedef struct {
    int type;
    int line;
    char *func;
    char *file;
    void *ptr;
    unsigned long size;
    long csum;
} MEMCHECK;

/* Last free pointers */
static MEMCHECK free_list[256];

static MEMCHECK alloc_list[MAX_ALLOC_LIST];
static int number_alloc_list = 0;
static int n = 0;               /* Alloc list pointer */
static int f = 0;               /* Free list pointer */

char *dbg_malloc(unsigned long size, char *file, char *function, int line)
{
    void *buf;
    int i = 0;
    long check;

    buf = zalloc(size + sizeof (long));

    check = 0xa5a5 + size;
    *(long *) ((char *) buf + size) = check;

    while (i < number_alloc_list) {
        if (alloc_list[i].type == 0)
            break;
        i++;
    }

    if (i == number_alloc_list)
        number_alloc_list++;

    assert(number_alloc_list < MAX_ALLOC_LIST);

    alloc_list[i].ptr = buf;
    alloc_list[i].size = size;
    alloc_list[i].file = file;
    alloc_list[i].func = function;
    alloc_list[i].line = line;
    alloc_list[i].csum = check;
    alloc_list[i].type = 9;

    if (debug & 1)
        printf("zalloc[%3d:%3d], %p, %4ld at %s, %3d, %s\n",
                i, number_alloc_list, buf, size, file, line,
                function);

    n++;
    return buf;
}

char *dbg_strdup(char *str, char *file, char *function, int line)
{
    void *buf;
    int i = 0;
    long check;
    long size;

    size = strlen(str) + 1;
    buf = zalloc(size + sizeof (long));
    strcat(buf, str);

    check = 0xa5a5 + size;
    *(long *) ((char *) buf + size) = check;

    while (i < number_alloc_list) {
        if (alloc_list[i].type == 0)
            break;
        i++;
    }

    if (i == number_alloc_list)
        number_alloc_list++;

    assert(number_alloc_list < MAX_ALLOC_LIST);

    alloc_list[i].ptr = buf;
    alloc_list[i].size = size;
    alloc_list[i].file = file;
    alloc_list[i].func = function;
    alloc_list[i].line = line;
    alloc_list[i].csum = check;
    alloc_list[i].type = 9;

    if (debug & 1)
        printf("strdup[%3d:%3d], %p, %4ld at %s, %3d, %s\n",
                i, number_alloc_list, buf, size, file, line,
                function);

    n++;
    return buf;
}



/* Display a buffer into a HEXA formated output */
static void dump_buffer(char *buff, int count)
{
    int i, j, c;
    int printnext = 1;

    if (count % 16)
        c = count + (16 - count % 16);
    else
        c = count;

    for (i = 0; i < c; i++) {
        if (printnext) {
            printnext--;
            printf("%.4x ", i & 0xffff);
        }
        if (i < count)
            printf("%3.2x", buff[i] & 0xff);
        else
            printf("   ");
        if (!((i + 1) % 8)) {
            if ((i + 1) % 16)
                printf(" -");
            else {
                printf("   ");
                for (j = i - 15; j <= i; j++)
                    if (j < count) {
                        if ((buff[j] & 0xff) >= 0x20
                                && (buff[j] & 0xff) <= 0x7e)
                            printf("%c",
                                    buff[j] & 0xff);
                        else
                            printf(".");
                    } else
                        printf(" ");
                    printf("\n");
                    printnext = 1;
            }
        }
    }
}

int dbg_free(void *buffer, char *file, char *function, int line)
{
    int i = 0;
    void *buf;

    /* If nullpointer remember */
    if (buffer == NULL) {
        i = number_alloc_list++;

        assert(number_alloc_list < MAX_ALLOC_LIST);

        alloc_list[i].ptr = buffer;
        alloc_list[i].size = 0;
        alloc_list[i].file = file;
        alloc_list[i].func = function;
        alloc_list[i].line = line;
        alloc_list[i].type = 2;
        if (debug & 1)
            printf("free NULL in %s, %3d, %s\n", file,
                    line, function);

        debug |= 512;   /* Memory Error detect */

        return n;
    } else
        buf = buffer;

    while (i < number_alloc_list) {
        if (alloc_list[i].type == 9 && alloc_list[i].ptr == buf) {
            if (*
                    ((long *) ((char *) alloc_list[i].ptr +
                        alloc_list[i].size)) ==
                    alloc_list[i].csum)
                alloc_list[i].type = 0; /* Release */
            else {
                alloc_list[i].type = 1; /* Overrun */
                if (debug & 1) {
                    printf("free corrupt, buffer overrun [%3d:%3d], %p, %4ld at %s, %3d, %s\n",
                            i, number_alloc_list,
                            buf, alloc_list[i].size, file,
                            line, function);
                    dump_buffer(alloc_list[i].ptr,
                            alloc_list[i].size + sizeof (long));
                    printf("Check_sum\n");
                    dump_buffer((char *) &alloc_list[i].csum,
                            sizeof(long));

                    debug |= 512;   /* Memory Error detect */
                }
            }
            break;
        }
        i++;
    }

    /*  Not found */
    if (i == number_alloc_list) {
        printf("Free ERROR %p\n", buffer);
        number_alloc_list++;

        assert(number_alloc_list < MAX_ALLOC_LIST);

        alloc_list[i].ptr = buf;
        alloc_list[i].size = 0;
        alloc_list[i].file = file;
        alloc_list[i].func = function;
        alloc_list[i].line = line;
        alloc_list[i].type = 4;
        debug |= 512;

        return n;
    }

    if (buffer != NULL)
        xfree(buffer);

    if (debug & 1)
        printf("free  [%3d:%3d], %p, %4ld at %s, %3d, %s\n",
                i, number_alloc_list, buf,
                alloc_list[i].size, file, line, function);

    free_list[f].file = file;
    free_list[f].line = line;
    free_list[f].func = function;
    free_list[f].ptr = buffer;
    free_list[f].type = 8;
    free_list[f].csum = i;  /* Using this field for row id */

    f++;
    f &= 255;
    n--;

    return n;
}

void dbg_free_final(char *banner)
{
    unsigned int sum = 0, overrun = 0, badptr = 0;
    int i, j;
    i = 0;

    printf("\n---[ Memory dump for (%s)]---\n\n", banner);

    while (i < number_alloc_list) {
        switch (alloc_list[i].type) {
            case 3:
                badptr++;
                printf
                    ("null pointer to realloc(nil,%ld)! at %s, %3d, %s\n",
                     alloc_list[i].size, alloc_list[i].file,
                     alloc_list[i].line, alloc_list[i].func);
                break;
            case 4:
                badptr++;
                printf
                    ("pointer not found in table to free(%p) [%3d:%3d], at %s, %3d, %s\n",
                     alloc_list[i].ptr, i, number_alloc_list,
                     alloc_list[i].file, alloc_list[i].line,
                     alloc_list[i].func);
                for (j = 0; j < 256; j++)
                    if (free_list[j].ptr == alloc_list[i].ptr)
                        if (free_list[j].type == 8)
                            printf
                                ("  -> pointer already released at [%3d:%3d], at %s, %3d, %s\n",
                                 (int) free_list[j].csum,
                                 number_alloc_list,
                                 free_list[j].file,
                                 free_list[j].line,
                                 free_list[j].func);
                break;
            case 2:
                badptr++;
                printf("null pointer to free(nil)! at %s, %3d, %s\n",
                        alloc_list[i].file, alloc_list[i].line,
                        alloc_list[i].func);
                break;
            case 1:
                overrun++;
                printf("%p [%3d:%3d], %4ld buffer overrun!:\n",
                        alloc_list[i].ptr, i, number_alloc_list,
                        alloc_list[i].size);
                printf(" --> source of malloc: %s, %3d, %s\n",
                        alloc_list[i].file, alloc_list[i].line,
                        alloc_list[i].func);
                break;
            case 9:
                sum += alloc_list[i].size;
                printf("%p [%3d:%3d], %4ld not released!:\n",
                        alloc_list[i].ptr, i, number_alloc_list,
                        alloc_list[i].size);
                printf(" --> source of malloc: %s, %3d, %s\n",
                        alloc_list[i].file, alloc_list[i].line,
                        alloc_list[i].func);
                break;
        }
        i++;
    }

    printf("\n\n---[ Memory dump summary for (%s) ]---\n", banner);
    printf("Total number of bytes not freed...: %d\n", sum);
    printf("Number of entries not freed.......: %d\n", n);
    printf("Maximum allocated entries.........: %d\n", number_alloc_list);
    printf("Number of bad entries.............: %d\n", badptr);
    printf("Number of buffer overrun..........: %d\n\n", overrun);

    if (sum || n || badptr || overrun)
        printf("=> Program seems to have some memory problem !!!\n\n");
    else
        printf("=> Program seems to be memory allocation safe...\n\n");
}

void *dbg_realloc(void *buffer, unsigned long size, char *file, char *function, int line)
{
    int i = 0;
    void *buf, *buf2;
    long check;

    if (buffer == NULL) {
        printf("realloc %p %s, %3d %s\n", buffer, file, line, function);
        i = number_alloc_list++;

        assert(number_alloc_list < MAX_ALLOC_LIST);

        alloc_list[i].ptr = NULL;
        alloc_list[i].size = 0;
        alloc_list[i].file = file;
        alloc_list[i].func = function;
        alloc_list[i].line = line;
        alloc_list[i].type = 3;
        return dbg_malloc(size, file, function, line);
    }

    buf = buffer;

    while (i < number_alloc_list) {
        if (alloc_list[i].ptr == buf) {
            buf = alloc_list[i].ptr;
            break;
        }
        i++;
    }

    /* not found */
    if (i == number_alloc_list) {
        printf("realloc ERROR no matching zalloc %p \n", buffer);
        number_alloc_list++;

        assert(number_alloc_list < MAX_ALLOC_LIST);

        alloc_list[i].ptr = buf;
        alloc_list[i].size = 0;
        alloc_list[i].file = file;
        alloc_list[i].func = function;
        alloc_list[i].line = line;
        alloc_list[i].type = 9;
        debug |= 512;   /* Memory Error detect */
        return NULL;
    }

    buf2 = ((char *) buf) + alloc_list[i].size;

    if (*(long *) (buf2) != alloc_list[i].csum) {
        alloc_list[i].type = 1;
        debug |= 512;   /* Memory Error detect */
    }
    buf = realloc(buffer, size + sizeof (long));

    check = 0xa5a5 + size;
    *(long *) ((char *) buf + size) = check;
    alloc_list[i].csum = check;

    if (debug & 1)
        printf("realloc [%3d:%3d] %p, %4ld %s %d %s -> %p %4ld %s %d %s\n",
                i, number_alloc_list, alloc_list[i].ptr,
                alloc_list[i].size, file, line, function, buf, size,
                alloc_list[i].file, alloc_list[i].line,
                alloc_list[i].func);

    alloc_list[i].ptr = buf;
    alloc_list[i].size = size;
    alloc_list[i].file = file;
    alloc_list[i].line = line;
    alloc_list[i].func = function;

    return buf;
}

#endif

